﻿//-------------------------------------------------------------------------------------------------
// <copyright file="SuperStar.cs" company="Anthony Adam">
// Copyright (c) Anthony Adam 2012. All rights reserved.
// </copyright>
//-------------------------------------------------------------------------------------------------
namespace SuperStarRounds
{
  using System;
  using System.Collections.Generic;
  using System.Drawing;
  using System.IO;
  using System.Linq;
  using System.Threading;
  using System.Windows.Forms;

  using Classes.FileIO;
  using Classes.Utility;
  using DockingTasks;

  using Models;

  using NHunspell;

  using WeifenLuo.WinFormsUI.Docking;

  /// <summary>
  /// Main application form class.
  /// </summary>
  public partial class SuperStar : Form
  {
    #region Internal Only - Create once instances

    /// <summary>
    /// Path to the Hunspell libraries for spelling and thesaurus engine dlls.
    /// </summary>
    private readonly string spellingPath = Application.StartupPath + "\\Hunspell\\";

    /// <summary>
    /// Reference to the US English spell checking engine
    /// </summary>
    public Hunspell SpellChecker { get; set; }

    /// <summary>
    /// Reference to the US Hyphenation engine
    /// </summary>
    // ReSharper disable once NotAccessedField.Local
    public Hyphen SpellHypenate { get; set; }

    /// <summary>
    /// Reference to the US Thesaurus engine
    /// </summary>
    public MyThes SpellThesaurus { get; set; }

    /// <summary>
    /// List of words added to the dictionary by user choice.
    /// </summary>
    public List<string> PersonalDictionary = new List<string>();

    /// <summary>
    /// Filepath to where the personal dictionary is saved to / loaded from.
    /// </summary>
    public readonly string PersonalDictionaryFile = Application.StartupPath + "\\Hunspell\\personal.dic";

    #endregion

    /// <summary>
    /// This will hold reference to the child docked tasks
    /// </summary>
    public List<BaseDockingTask> DockedTasks { get; set; } = new List<BaseDockingTask>();

    public ContentConfiguration ContentConfigurationPanel { get; set; }

    public DesignAdvice DesignerAdvicePanel { get; set; }

    // Allow the tasks to set the helper text
    public string HelperTextRtf
    {
      get
      {
        if (DesignerAdvicePanel == null) return string.Empty;
        return DesignerAdvicePanel.IsDisposed ? string.Empty : DesignerAdvicePanel.HelperTextRtf;
      }
      set
      {
        if (DesignerAdvicePanel == null) return;
        if (DesignerAdvicePanel.IsDisposed) return;

        DesignerAdvicePanel.HelperTextRtf = value;
      }
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="SuperStar" /> class.
    /// </summary>
    public SuperStar()
    {
      InitializeComponent();
    }

    /// <summary>
    /// Adds the word provided to the personal dictionary. It also immediately
    /// saves the updated personal dictionary in case of power outages, etc.
    /// </summary>
    /// <param name="word">The word to be added to the personal dictionary</param>
    public void AddToPersonalDictionary(string word)
    {
      PersonalDictionary.Add(word);
      PersonalDictionarySerializer.SerializePersonalDictionary(PersonalDictionary, PersonalDictionaryFile);
    }

    private ContentInclusions GetInclusionCheckState()
    {
      var inclusions = new ContentInclusions();
      if (ContentConfigurationPanel == null) return inclusions;

      return ContentConfigurationPanel.IsDisposed ? inclusions : ContentConfigurationPanel.GetInclusionCheckState();
    }

    private void SetInclusionCheckState(ContentInclusions states)
    {
      if (ContentConfigurationPanel == null) return;
      if (ContentConfigurationPanel.IsDisposed) return;

      ContentConfigurationPanel.SetInclusionCheckState(states);
    }

    /// <summary>
    /// Flag event handler to set the language of the client application.
    /// This does not change the expectation of en-US entry within the controls,
    /// it only localises the help and control labelling in the client.
    /// </summary>
    /// <param name="sender">Holds the object instantiating this event</param>
    /// <param name="e">Holds the event arguments</param>
    private void BtnSetLanguage_Click(object sender, EventArgs e)
    {
      var myLanguageButton = sender as ToolStripItem;
      if (myLanguageButton == null) return;

      foreach (var loadedTask in DockedTasks)
      {
        if (loadedTask.IsDisposed) continue;
        loadedTask.SetLanguage(myLanguageButton.Tag.ToString());
      }

    }

    /// <summary>
    /// Provides a standard event for controls with effects that may alter selection
    /// content and configuration of the application.
    /// </summary>
    /// <param name="sender">Holds the object instantiating this event</param>
    /// <param name="e">Holds the event arguments</param>
    public void ResetContent(object sender, EventArgs e)
    {
      foreach (var panel in DockedTasks)
      {
        if (panel == null) continue;
        if (panel.IsDisposed) continue;
        if (panel.Name.Equals("ContentConfiguration")) continue; // avoid recursion!
        panel.ResetContent(sender, e);
      }
    }

    /// <summary>
    /// Localises the form text of the application using localization strings
    /// held in localisation resource files.
    /// </summary>
    /// <param name="resetAdvice">Reset the advice panel to default if true</param>
    private void LocaliseForm(bool resetAdvice)
    {
      // Form Title and Build
      Text = Localize.FormLabel;
      lblBuild.Text = Localize.VersionVersion + FormUtility.Spacer() + Application.ProductVersion + FormUtility.Spacer() +
                      Localize.VersionAlpha;

      // ToolBar 
      btnDutch.ToolTipText = Localize.DutchButtonLabel;
      btnEnglish.ToolTipText = Localize.EnglishButtonLabel;
      btnFrench.ToolTipText = Localize.FrenchButtonLabel;
      btnGerman.ToolTipText = Localize.GermanButtonLabel;
      btnGreek.ToolTipText = Localize.GreekButtonLabel;
      btnItalian.ToolTipText = Localize.ItalianButtonLabel;
      btnRussian.ToolTipText = Localize.RussianButtonLabel;
      btnSpanish.ToolTipText = Localize.SpanishButtonLabel;

      // Advisor Panel Reset only if asked for.
      if (resetAdvice)
      {
        // default is advisor off
        if (!(DesignerAdvicePanel == null || DesignerAdvicePanel.IsDisposed)) DesignerAdvicePanel.Close();
        DesignerAdvicePanel = null;
      }

      BtnShowHideAdvisorPanel.Text = (DesignerAdvicePanel == null || DesignerAdvicePanel.IsDisposed)
        ? Localize.ShowAdvisorLabel
        : Localize.HideAdvisorLabel;

      foreach (var panel in DockedTasks)
      {
        if (panel == null) continue;
        if (panel.IsDisposed) continue;

        panel.LocaliseForm();
      }
    }

    /// <summary>
    /// Once the form has loaded, additional start up tasks are performed here.
    /// These tasks are the loading of the personal dictionary, setting the initial
    /// default language, populating the choices in the combo boxes and updating the
    /// status of the splash screen as the initialization tasks complete.
    /// </summary>
    /// <param name="sender">Holds the object instantiating this event</param>
    /// <param name="e">Holds the event arguments</param>
    private void SuperStar_Load(object sender, EventArgs e)
    {
      // loading mdi children maximised has a known issue that messes with icons - this is the workaround.
      Icon = Icon?.Clone() as Icon;

      SuperStarSplash.SuperStarSplash.ShowSplashScreen();
      SuperStarSplash.SuperStarSplash.SetStatus("Linking US Dictionary and Thesaurus");

      // Activate Hunspell spell checking
      Hunspell.NativeDllPath = spellingPath;
      SpellChecker = new Hunspell(spellingPath + "en_US.aff", spellingPath + "en_US.dic");
      SpellHypenate = new Hyphen(spellingPath + "hyph_en_US.dic");
      SpellThesaurus = new MyThes(spellingPath + "th_en_US_new.dat");

      // Delay added so splash is readable for faster machines
      // Added to main form so that progress bar on sleep form animates smoothly
      Thread.Sleep(750);
      SuperStarSplash.SuperStarSplash.SetStatus("Linking Private Dictionary");

      // Load the Personal Dictionary if created
      PersonalDictionary = PersonalDictionarySerializer.DeserializePersonalDictionary(PersonalDictionaryFile).ToList();
      foreach (var word in PersonalDictionary)
      {
        SpellChecker.Add(word);
      }

      // Delay added so splash is readable for faster machines
      // Added to main form so that progress bar on sleep form animates smoothly
      Thread.Sleep(750);

      SuperStarSplash.SuperStarSplash.SetStatus("Loading Last Used Layouts");
      LoadDockPanelLayout(dockPanel1);

      // Ensure focus is on content configuration after loading the panels, which
      // also ensures we always have the content configuration loaded even if the
      // layout xml removed it - it is mandatory to exist for app operation.
      launchContentConfiguration_Click(sender, e);

      SuperStarSplash.SuperStarSplash.SetStatus("Loading Last Used Content Settings");
      SetInclusionCheckState(RepositoryHandler.LoadLastUsedInclusion());

      SuperStarSplash.SuperStarSplash.SetStatus("Localising Forms");
      // Set default Language to begin with (no params is default language en-US)
      Localize.SetLanguage();
      LocalizeAdvisor.SetLanguage();

      // localise the form and controls:
      LocaliseForm(true);

      // Delay added so splash is readable for faster machines
      // Added to main form so that progress bar on sleep form animates smoothly
      Thread.Sleep(750);
      SuperStarSplash.SuperStarSplash.SetStatus("Establishing My Document Repository");

      // Ensure our storage area is created in My Documents
      RepositoryHandler.CreateRepositoryStructure();

      // Delay added so splash is readable for faster machines
      // Added to main form so that progress bar on sleep form animates smoothly
      Thread.Sleep(750);

      SuperStarSplash.SuperStarSplash.CloseSplash();
    }

    /// <summary>
    /// As the application closes, when the form close event is triggered, any
    /// personal dictionary is written to be loaded back the next time.
    /// </summary>
    /// <param name="sender">Holds the object instantiating this event</param>
    /// <param name="e">Holds the event arguments</param>
    private void SuperStar_FormClosing(object sender, FormClosingEventArgs e)
    {
      try
      {
        // preserve panel layout
        SaveDockPanelLayout(dockPanel1);
      }
      catch (Exception)
      {
        // Any failure means do not updated
      }
      try
      {
        // content config state.
        RepositoryHandler.SaveLastUsedInclusion(GetInclusionCheckState());
      }
      catch (Exception)
      {
        // Any failure means do not updated
      }

      // Save any personal dictionary words - this is all invisible to the user.
      if (PersonalDictionary.Count <= 0) return;
      PersonalDictionarySerializer.SerializePersonalDictionary(PersonalDictionary, PersonalDictionaryFile);
    }

    public void LoadDockPanelLayout(DockPanel dockPanel)
    {
      if (string.IsNullOrWhiteSpace(RepositoryHandler.DocumentRepository))
        RepositoryHandler.CreateRepositoryStructure();

      var directory = Path.Combine(RepositoryHandler.DocumentRepository, RepositoryHandler.SettingsDirectory);
      if (!Directory.Exists(directory)) return;

      var file = Path.Combine(RepositoryHandler.DocumentRepository, RepositoryHandler.SettingsDirectory,
        RepositoryHandler.DockingLayoutFile);
      if (!File.Exists(file)) return;

      try
      {
        DockedTasks = new List<BaseDockingTask>();
        dockPanel.LoadFromXml(file, DeserializeContent);
      }
      catch (Exception)
      {
        // default to initial default layout - with content configuration loaded...
        if (DockedTasks.Any(loadedForm => loadedForm.Name.Equals("ContentConfiguration"))) return;

        LaunchPanel<ContentConfiguration>();
      }
    }

    public void SaveDockPanelLayout(DockPanel dockPanel)
    {
      if (string.IsNullOrWhiteSpace(RepositoryHandler.DocumentRepository))
        RepositoryHandler.CreateRepositoryStructure();

      var directory = Path.Combine(RepositoryHandler.DocumentRepository, RepositoryHandler.SettingsDirectory);
      if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);

      var file = Path.Combine(RepositoryHandler.DocumentRepository, RepositoryHandler.SettingsDirectory,
        RepositoryHandler.DockingLayoutFile);
      try
      {
        dockPanel.SaveAsXml(file);
      }
      catch (Exception)
      {
        // next load, we default to initial default layout as xml is corrupt in some way.
        if (!File.Exists(file)) File.Delete(file);
      }
    }

    private IDockContent DeserializeContent(string persistString)
    {
      switch (persistString)
      {
        case "SuperStarRounds.DockingTasks.ContentConfiguration":
          {
            // only one config allowed...
            foreach (var loadedTask in DockedTasks)
            {
              if (!loadedTask.Name.Equals("ContentConfiguration")) continue;

              ContentConfigurationPanel = (ContentConfiguration)loadedTask;
              return loadedTask;
            }

            ContentConfigurationPanel = LoadPanel<ContentConfiguration>();
            return ContentConfigurationPanel;
          }

        case "SuperStarRounds.DockingTasks.CreateWondrousItem":
          {
            return LoadPanel<CreateWondrousItem>();
          }

        case "SuperStarRounds.DockingTasks.DesignAdvice":
          {
            // only one config allowed...
            foreach (var loadedTask in DockedTasks)
            {
              if (!loadedTask.Name.Equals("DesignAdvice")) continue;

              DesignerAdvicePanel = (DesignAdvice)loadedTask;
              return loadedTask;
            }

            DesignerAdvicePanel = LoadPanel<DesignAdvice>();
            return DesignerAdvicePanel;
          }

        default:
          return null;
      }
    }

    /// <summary>
    /// Load a task panel into the collection of loaded panels from layout restore.
    /// </summary>
    /// <typeparam name="T">Task Panel Type</typeparam>
    /// <returns>Instance of the correct dockable Task</returns>
    private T LoadPanel<T>() where T : new()
    {
      var panel = new T();
      var basePanel = panel as BaseDockingTask;
      if (basePanel == null) return panel;

      basePanel.OwnerSuperStar = this;
      DockedTasks.Add(basePanel);
      return panel;
    }

    /// <summary>
    /// Load a task panel into the collection of loaded panels from menu selections.
    /// </summary>
    /// <typeparam name="T">Task Panel Type</typeparam>
    private T LaunchPanel<T>() where T : new()
    {
      var panel = new T();
      var basePanel = panel as BaseDockingTask;
      if (basePanel == null) return panel;

      basePanel.OwnerSuperStar = this;
      basePanel.Show(dockPanel1, DockState.Document,
        new Rectangle(basePanel.Location.X, basePanel.Location.Y, basePanel.Width, basePanel.Height));
      DockedTasks.Add(basePanel);
      return panel;
    }

    private void launchContentConfiguration_Click(object sender, EventArgs e)
    {
      ContentConfigurationPanel = DockedTasks
        .Where(loadedTask => loadedTask.Name.Equals("ContentConfiguration"))
        .Where(loadedTask => !loadedTask.IsDisposed)
        .Cast<ContentConfiguration>().FirstOrDefault() ?? LaunchPanel<ContentConfiguration>();
      ContentConfigurationPanel.Show();
    }

    private void launchWondrousItem_Click(object sender, EventArgs e)
    {
      LaunchPanel<CreateWondrousItem>();
    }

    private void BtnShowHideAdvicePanel_Click(object sender, EventArgs e)
    {
      if (BtnShowHideAdvisorPanel.Text.Equals(Localize.ShowAdvisorLabel))
        ShowAdvicePanel();
      else
        HideAdvicePanel();
    }

    private void ShowAdvicePanel()
    {
      DesignerAdvicePanel = DockedTasks
        .Where(loadedTask => loadedTask.Name.Equals("DesignAdvice"))
        .Where(loadedTask => !loadedTask.IsDisposed)
        .Cast<DesignAdvice>().FirstOrDefault() ?? LaunchPanel<DesignAdvice>();
      DesignerAdvicePanel.Show();
      BtnShowHideAdvisorPanel.Text = Localize.HideAdvisorLabel;
    }

    private void HideAdvicePanel()
    {
      foreach (var loadedTask in DockedTasks)
      {
        if (!loadedTask.Name.Equals("DesignAdvice")) continue;
        if (loadedTask.IsDisposed) continue;

        loadedTask.Close();
        break;
      }

      DesignerAdvicePanel = null;
      BtnShowHideAdvisorPanel.Text = Localize.ShowAdvisorLabel;
    }
  }
}
